#include <nds.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#include "screen.h"
#include "poker_eval.h"
#include "tiles.h"
#include "wifi.h"

char CARDS[] =         {167,168,'\0'};
char BUTTON[] =        {190,'\0'};
char BTN_NUMBERS0[] =  {169,170,171,172,173,174,'\0'};
char BTN_NUMBERS1[] =  {175,176,177,178,179,180,'\0'};
char BTN_NUMBERS2[] =  {181,182,183,184,185,186,'\0'};
char BTN_NUMBERS3[] =  {213,214,215,216,217,218,'\0'};
char BTN_SLIDER1[]  =  {219,220,'\0'};
char BTN_SLIDER2[]  =  {221,'\0'};
char BTN_SLIDER2b[]  = {222,'\0'};
char BTN_SLIDER3[]  =  {223,224,'\0'};
char BTN_FOLD[] =      {128,129,130,131,132,133,'\0'};
char BTN_RAISE[] =     {134,135,136,137,138,139,'\0'};
char BTN_CHECK[] =     {140,141,142,143,144,145,'\0'};
char BTN_CALL[] =      {146,147,148,149,150,151,'\0'};
char BTN_BET[] =       {152,153,154,155,156,157,'\0'};
char BTN_POT[] =       {192,193,194,195,196,197,'\0'};
char BTN_23POT[] =     {198,199,200,201,202,203,'\0'};
char BTN_34POT[] =     {204,205,206,201,202,203,'\0'};
char BTN_ALLIN[] =     {207,208,209,210,211,212,'\0'};
char BTN_REPLAY[] =    {232,233,'\0'};
char BTN_RABBIT[] =    {234,235,236,'\0'};
char BTN_QUIT[] =      {237,238,239,240,241,242,'\0'};
char BTN_SITOUT[] =    {245,246,247,248,249,'\0'};
char BTN_UNCHECKED[] = {243, '\0'};
char BTN_CHECKED[] =   {244, '\0'};
char PERCENT =         251;


struct SEAT {
    int PlayerX;
    int PlayerY;
    int Width;
    int BetX;
    int BetY;
    int CardX;
    int CardY;
    int ButtonX;
    int ButtonY;
    int ActionX;
    int ActionY;
};

SEAT SixSeats[MAX_PLAYERS] = {   // PX   PY   W  BX  BY  CX  CY  BX  BY  AX  AY
                                  {  0,  0, -9, 10,  4, 10,  2,  8,  2, 12,  2 },   // 0
                                  { 23,  0,  9, 17,  4, 20,  2, 23,  2, 19,  2 },   // 1
                                  { 24, 10,  8, 18, 12, 21, 10, 24, 12, 20, 10 },   // 2
                                  { 23, 18,  9, 17, 20, 20, 18, 23, 20, 19, 18 },   // 3
                                  {  0, 18, -9, 10, 20, 10, 18,  8, 20, 12, 18 },   // 4
                                  {  0, 10, -8,  9, 12,  9, 10,  7, 12, 11, 10 }    // 5
};

SEAT EightSeats[MAX_PLAYERS] = { // PX   PY   W  BX  BY  CX  CY  BX  BY  AX  AY
                                  {  0,  0, -9, 10,  2, 10,  0,  8,  0, 12,  0 },   // 0
                                  { 23,  0,  9, 17,  2, 20,  0, 23,  0, 19,  0 },   // 1
								  { 24,  5,  8, 18,  7, 21,  5, 24,  7, 20,  5 },   // 2
								  { 24, 15,  8, 18, 17, 21, 15, 24, 17, 20, 15 },   // 3
								  { 23, 20,  9, 17, 22, 20, 20, 23, 22, 19, 20 },   // 4
                                  {  0, 20, -9, 10, 22, 10, 20,  8, 22, 12, 20 },   // 5
								  {  0, 15, -8,  9, 17,  9, 15,  7, 17, 11, 15 },   // 6
								  {  0,  5, -8,  9,  7,  9,  5,  7,  7, 11,  5 }    // 7
};

SEAT *Seats;

bool Inverse;
int bg0, bg1;
u16* subMap;
u16* subMap2;
u16* map;

extern GAME Game;

extern int Blind;
extern int Pot;
extern int ButtonPos;
extern int LevelStartTime;
extern int MinsPerLevel;
extern int ThisPlayer;
extern bool HoleCardsShowing;
extern GameMode Mode;
extern int NumCards;
extern bool CheckFold;
extern bool SitOut;
extern bool ShowingReplay;

int topchar(int c)
{
    int result;
    
    if(c >= 32)
    {
        // double-height characters.
        result = c - 32;
        result = (result % 40 + (80*(int)(result/40)) + 40) | (Inverse ? 1 << 12 : 0);
    }
    else
    {
        // Single-height characters.
        result = (c + 8) | (Inverse ? 1 << 12 : 0);
    }
    return result;
}

int bottomchar(int c)
{
    return (topchar(c) + 40) | (Inverse ? 1 << 12 : 0);
}

void prints(int screen, int x, int y, const char *fmt, ...)
{
    static char buff[1024];
    char *s = buff;
    u16* curmap;

    va_list arg;
    va_start(arg, fmt);
    vsprintf(buff, fmt, arg);
    va_end(arg);

    if(screen)
        curmap = subMap;
    else
        curmap = map;

    while(*s)
    {
        curmap[x + (y * 32)] = topchar(*s);
        if(*s >= 32)
        {
            curmap[x + (y * 32 + 32)] = bottomchar(*s);
        }
        x++;
        s++;
    }
}

void DisplayMessage(const char *fmt, ...)
{
    static char buff[1024];

    va_list arg;
    va_start(arg, fmt);
    vsprintf(buff, fmt, arg);
    va_end(arg);

    prints(BOTTOM, 0, 22, "%-32s",buff);
}

void ClearMessage(void)
{
    DisplayMessage("");
}

void ClearRows(int screen, int y1, int y2)
{
    for( ; y1 <= y2 ; y1++)
    {
        prints(screen, 0, y1, "%32s", "");
    }
}

void Cls(int screen)
{
    ClearRows(screen, 0, 22);
}

void HorizLine(int screen, int x, int y, int width, bool bottom)
{
    u16* curmap;

    if(screen)
    {
        curmap = subMap;
    }
    else
    {
        curmap = map;
    }

    while(width)
    {
        if(bottom)
        {
            curmap[x + (y * 32)] = topchar(166) | 1 << 11;
        }
        else
        {
            curmap[x + (y * 32)] = topchar(166);
        }
        x++;
        width--;
    }
}

void DoubleHorizLine(int screen, int x, int y, int width)
{
    u16* curmap;

    if(screen)
    {
        curmap = subMap;
    }
    else
    {
        curmap = map;
    }

    while(width)
    {
        curmap[x + (y * 32)] = topchar(250);
        x++;
        width--;
    }
}

void VertLine(int screen, int x, int y, int height, bool left)
{
    u16* curmap;

    if(screen)
    {
        curmap = subMap;
    }
    else
    {
        curmap = map;
    }

    while(height)
    {
        if(left)
        {
            curmap[x + (y * 32)] = bottomchar(166) | 1 << 10;
        }
        else
        {
            curmap[x + (y * 32)] = bottomchar(166);
        }
        y++;
        height--;
    }
}

void DisplayKeys(int x, int y, const char *row)
{
    uint f;
    for(f = 0 ; f < strlen(row) ; f++)
    {
        VertLine(BOTTOM, x - 1, y, 2, false);
        VertLine(BOTTOM, x + 1, y, 2, true);
        HorizLine(BOTTOM, x - 1, y - 1, 3, true);
        HorizLine(BOTTOM, x - 1, y + 2, 3, false);
        prints(BOTTOM, x, y, "%c", row[f]);
        x += 3;
    }
}

int MapSeat(int player)
{
    if(Mode == gm_Standalone)
    {
        return player;
    }
    return (player + Game.MaxPlayers - ThisPlayer) % Game.MaxPlayers;
}

void DisplayOdds(int player, double percent)
{
    int x,y;

    x = Seats[MapSeat(player)].BetX;
    y = Seats[MapSeat(player)].BetY;

    if(percent == -1)
    {
        prints(TOP, x, y, "     ");
    }
    else
    {
        char str[10];
        sprintf(str, "%.0f%c", percent, PERCENT);

        if(Seats[MapSeat(player)].Width > 0)
        {
            prints(TOP, x, y, "%5s", str);
        }
        else
        {
            prints(TOP, x, y, "%-5s", str);
        }
    }
}

void DisplayBet(int player, int bet)
{
    int x,y;

    x = Seats[MapSeat(player)].BetX;
    y = Seats[MapSeat(player)].BetY;

    if(bet)
    {
        if(Seats[MapSeat(player)].Width > 0)
        {
            prints(TOP, x, y, "%5d", bet);
        }
        else
        {
            prints(TOP, x, y, "%-5d", bet);
        }
    }
    else
    {
        prints(TOP, x, y, "     ");
    }
}

void DisplayAction(int player, bool show)
{
    prints(TOP, Seats[MapSeat(player)].ActionX, Seats[MapSeat(player)].ActionY, "%c", show ? Game.Players[player].Action : ' ');
}

void DisplayCards(int player, bool show)
{
    int temp = Inverse;

    // Cards should never be shown highlighted.
    Inverse = false;
    prints(TOP, Seats[MapSeat(player)].CardX, Seats[MapSeat(player)].CardY, "%s", show ? CARDS : "  ");
    Inverse = temp;
}

int TimeLeft(void)
{
    return Game.LevelStartTime + (MinsPerLevel * SECS_PER_MIN) - TimeInSeconds();
}

void DisplayPot(void)
{
    Inverse = 1;
    prints(BOTTOM, 0, 0, "%4d/%-4d  Pot:%-7d",Game.Blind / 2, Game.Blind, Game.Pot);

    int secsLeft = TimeLeft();

    if(secsLeft < 0)
    {
        prints(BOTTOM, 22, 0, " Next Hand");
    }
    else
    {
        char temp[32];

        sprintf(temp, "<%d min", (secsLeft - 1) / SECS_PER_MIN + 1);
        if(secsLeft > SECS_PER_MIN)
        {
            strcat(temp,"s");
        }
        prints(BOTTOM, 22, 0, "%10s", temp);
    }
    Inverse = 0;
}

void DisplayPlayer(int player, bool inverse)
{
    int x,y;

    if(inverse)
    {
        Inverse = 1;
    }
	x = Seats[MapSeat(player)].PlayerX;
    y = Seats[MapSeat(player)].PlayerY;

    prints(TOP, x, y, "%*s", Seats[MapSeat(player)].Width, Game.Players[player].Name);

    if(Game.AllIn && !Game.Players[player].Folded)
    {
        RevealHoleCards(player);
    }
    else
    {
        if(Game.Players[player].Chips)
        {
            prints(TOP, x, y + 2, "%*d", Seats[MapSeat(player)].Width , Game.Players[player].Chips);
        }
        else
        {
            if(Game.Players[player].SitOutHands)
            {
                char hands[10];
                sprintf(hands,"Out:%d",Game.Players[player].SitOutHands);
                prints(TOP, x, y + 2, "%*s", Seats[MapSeat(player)].Width, hands);
            }
            else
            {
                prints(TOP, x, y + 2, "%*s", Seats[MapSeat(player)].Width, "");
            }
        }
        if(Game.MaxPlayers < MAX_PLAYERS)
		{
			prints(TOP, x, y + 4, "%*s", Seats[MapSeat(player)].Width, "");
		}
    }

    // Dealer?
    if(player == Game.ButtonPos)
    {
        prints(TOP, Seats[MapSeat(player)].ButtonX, Seats[MapSeat(player)].ButtonY, "%s", BUTTON);
    }

    Inverse = 0;

    DisplayAction(player, true);
}

void HighlightAllPlayers(bool highlight)
{
    int f;

    for(f = 0 ; f < Game.MaxPlayers ; f++)
    {
        DisplayPlayer(f, highlight);
    }
}

void DisplayCurrentBet(int bet, int player)
{
    int f;
    int pos;

    if(!Game.Players[player].Folded)
    {
        if(player == Game.ActionOnPlayer)
        {
            Inverse = 1;
        }
        if(bet)
        {
            prints(BOTTOM, 19, 8, "%6d", bet);
        }
        else
        {
            prints(BOTTOM, 19, 8, "      ");
        }
        Inverse = 0;

        for(f = 0 ; f < SLIDER_WIDTH ; f++)
        {
            // Clear slider
            prints(BOTTOM, f + 14, 19, "%s", BTN_SLIDER2);
        }

        if(bet)
        {
            // Position slider marker
            pos = bet * (SLIDER_WIDTH - 1) / (Game.Players[player].Chips + Game.Players[player].Bet);
            prints(BOTTOM, pos + 14, 19, "%s", BTN_SLIDER2b);
        }
    }
}

bool CanHunt(int player)
{
    if(player != Game.ActionOnPlayer)
    {
        return false;
    }

    int f;
    int playersRemaining = 0;
    for(f = 0 ; f < Game.MaxPlayers ; f++)
    {
        if(!Game.Players[f].Folded)
        {
            playersRemaining++;
        }
    }

    return (Game.CardsShowing == 4 && playersRemaining == 2 && !Game.RabbitHunt && Game.Players[player].RabbitHunts > 0);
}

void DisplayBetOptions(int player, int minBet, int lastRaise, int currentBet, bool overrideMinBet)
{
    char temp[32], temp2[32];
    int f;

    if(!Game.AllIn && !Game.Players[ThisPlayer].Folded && !Game.Players[ThisPlayer].SitOutHands)
    {
        // Box around bet amount
        HorizLine(BOTTOM, 19, 7, 6, true);
        VertLine(BOTTOM, 18, 8, 2, true);
        VertLine(BOTTOM, 25, 8, 2, false);

        DisplayCurrentBet(currentBet, ThisPlayer);

        prints(BOTTOM, 19, 10, "%s", BTN_NUMBERS0);
        prints(BOTTOM, 19, 12, "%s", BTN_NUMBERS1);
        prints(BOTTOM, 19, 14, "%s", BTN_NUMBERS2);
        prints(BOTTOM, 19, 16, "%s", BTN_NUMBERS3);

        // Slider
        prints(BOTTOM, 12, 19, "%s", BTN_SLIDER1);
        for(f = 14 ; f < 30 ; f++)
        {
            prints(BOTTOM, f, 19, "%s", BTN_SLIDER2);
        }
        prints(BOTTOM, f, 19, "%s", BTN_SLIDER3);

        prints(BOTTOM, 0, 16, "%s", BTN_FOLD);
        DisplayCheckBox(BOTTOM, 6, 16, CheckFold);
    }
    else if(Game.Players[ThisPlayer].Folded)
    {
        prints(BOTTOM, 6, 16, " ");  // CheckFold
    }

    if(Mode != gm_Standalone)
    {
        prints(BOTTOM, 0, 7, "%s", BTN_SITOUT);
        DisplayCheckBox(BOTTOM, 5, 7, SitOut);
    }

    if(player == Game.ActionOnPlayer && !Game.Players[ThisPlayer].SitOutHands)
    {
        if(Mode == gm_Standalone)
        {
            prints(BOTTOM, 0, 4, "%s", BTN_REPLAY);
            prints(BOTTOM, 26, 7, "%s", BTN_QUIT);
        }
        else
        {
            prints(BOTTOM, 0, 4, "  "); // Remove REPLAY
        }
        prints(BOTTOM, 0, 19, "%s", CanHunt(player) ? BTN_RABBIT : "   ");

        // Check or call button.
        if(Game.Players[player].CanCheck)
        {
            prints(BOTTOM, 0, 10, "            %s", BTN_CHECK);
        }
        else
        {
            if(minBet <= Game.Players[player].Chips + Game.Players[player].Bet 
                && minBet - Game.Players[player].Bet != 0)
            {
                sprintf(temp,"(%d)", minBet);
                if(Game.Players[player].Bet)
                {
                    sprintf(temp2,"+%-4d", minBet - Game.Players[player].Bet);
                }
                else
                {
                    strcpy(temp2,"     ");
                }
                prints(BOTTOM, 0, 10, "%s%7s%s", temp2, temp, BTN_CALL);
            }
            else
            {
                if(minBet - Game.Players[player].Bet != 0)
                {
                    prints(BOTTOM, 0, 10, "                  ");
                }
                else
                {
                    prints(BOTTOM, 0, 10, "            %s", BTN_CALL);
                }
            }
        }

        // Bet or raise button.
        if(!lastRaise)
        {
            // No blinds in and nobody has bet.  Bet button.
            if(overrideMinBet)
            {
                minBet = currentBet;
            }
            sprintf(temp,"(%d)", minBet);
            prints(BOTTOM, 0, 13, "     %7s%s", temp, BTN_BET);
        }
        else
        {
            if(overrideMinBet)
            {
                minBet = currentBet - lastRaise;
            }

            if(minBet + lastRaise <= Game.Players[player].Chips
                && minBet - Game.Players[player].Bet != 0)
            {
				if(minBet + lastRaise < 10000)
				{
					sprintf(temp,"(%d)", minBet + lastRaise);
				}
				else
				{
					sprintf(temp," %d", minBet + lastRaise);
				}

                if(Game.Players[player].Bet)
                {
                    sprintf(temp2,"+%-4d", minBet + lastRaise - Game.Players[player].Bet);
                }
                else
                {
                    strcpy(temp2,"     ");
                }
                prints(BOTTOM, 0, 13, "%s%*s%s", temp2, (minBet + lastRaise < 10000 ? 7 : 6), temp, BTN_RAISE);
            }
            else
            {
                if(minBet - Game.Players[player].Bet != 0)
                {
                    prints(BOTTOM, 0, 13, "                  ");
                }
                else
                {
                    prints(BOTTOM, 0, 13, "            %s", BTN_RAISE);
                }
            }
        }

        prints(BOTTOM, 12, 16, "%s", BTN_ALLIN);
        prints(BOTTOM, 26, 10, "%s", BTN_23POT);
        prints(BOTTOM, 26, 13, "%s", BTN_34POT);
        prints(BOTTOM, 26, 16, "%s", BTN_POT);
    }
    else
    {
        if(player == -1)
        {
            prints(BOTTOM, 0, 4, "  "); // Remove REPLAY
        }
        else
        {
            prints(BOTTOM, 0, 4, "%s", BTN_REPLAY);
        }
        prints(BOTTOM, 0, 19, "   ");
        prints(BOTTOM, 0, 10, "                  ");
        prints(BOTTOM, 0, 13, "                  ");
        prints(BOTTOM, 12, 16, "      ");
        prints(BOTTOM, 26, 10, "      ");
        prints(BOTTOM, 26, 13, "      ");
        prints(BOTTOM, 26, 16, "      ");
    }
}

void DrawTable(void)
{
	if(Game.MaxPlayers == MAX_PLAYERS)
	{
		// Boxes around top two players
		HorizLine(TOP, 8, 4, 1, false);
		HorizLine(TOP, 23, 4, 1, false);
		VertLine(TOP, 9, 0, 4, false);
		VertLine(TOP, 22, 0, 4, true);

		// Boxes around next two players
		DoubleHorizLine(TOP, 0, 4, 8);
		DoubleHorizLine(TOP, 24, 4, 8);
		VertLine(TOP, 8, 5, 4, false);
		VertLine(TOP, 23, 5, 4, true);
		HorizLine(TOP, 0, 9, 8, false);
		HorizLine(TOP, 24, 9, 8, false);

		// Boxes around next two players
		HorizLine(TOP, 0, 14, 8, true);
		HorizLine(TOP, 24, 14, 8, true);
		VertLine(TOP, 8, 15, 4, false);
		VertLine(TOP, 23, 15, 4, true);
		DoubleHorizLine(TOP, 0, 19, 8);
		DoubleHorizLine(TOP, 24, 19, 8);

		// Boxes around bottom two players
		HorizLine(TOP, 8, 19, 1, true);
		HorizLine(TOP, 23, 19, 1, true);
		VertLine(TOP, 9, 20, 4, false);
		VertLine(TOP, 22, 20, 4, true);
	}
	else
	{
		// Boxes around top two players
		HorizLine(TOP, 0, 6, 9, false);
		HorizLine(TOP, 23, 6, 9, false);
		VertLine(TOP, 9, 0, 6, false);
		VertLine(TOP, 22, 0, 6, true);

		// Boxes around middle players
		HorizLine(TOP, 0, 9, 8, true);
		HorizLine(TOP, 24, 9, 8, true);
		VertLine(TOP, 8, 10, 6, false);
		VertLine(TOP, 23, 10, 6, true);
		HorizLine(TOP, 0, 16, 8, false);
		HorizLine(TOP, 24, 16, 8, false);

		// Boxes around bottom two players
		HorizLine(TOP, 0, 17, 9, true);
		HorizLine(TOP, 23, 17, 9, true);
		VertLine(TOP, 9, 18, 6, false);
		VertLine(TOP, 22, 18, 6, true);
	}
	
	// Line below pot
    HorizLine(BOTTOM, 0, 2, 32, false);

    // Line above message text
    HorizLine(BOTTOM, 0, 21, 32, true);

    if(!Game.AllIn)
    {
        // Box around community cards
        HorizLine(BOTTOM, 12, 3, 19, true);
        HorizLine(BOTTOM, 12, 6, 19, false);
        VertLine(BOTTOM, 11, 4, 2, true);
        VertLine(BOTTOM, 31, 4, 2, false);

        // Box around hole cards
        HorizLine(BOTTOM, 3, 3, 7, true);
        HorizLine(BOTTOM, 3, 4 + NumCards, 7, false);
        VertLine(BOTTOM, 2, 4, NumCards, true);
        VertLine(BOTTOM, 10, 4, NumCards, false);
    }
}

void DisplayCheckBox(int screen, int x, int y, bool checked)
{
    prints(screen, x, y, "%s", checked ? BTN_CHECKED : BTN_UNCHECKED);
}

// Show the player their hole cards in the bottom window.
void DisplayHoleCards(int player)
{
    char handStr[10];

    if(player != -1 && HoleCardsShowing && Game.Players[player].SittingIn && !SitOut)
    {
        if(Game.Players[player].Folded)
        {
            ConvertHandSmall(Game.Players[player].Cards, 2, handStr);
            prints(BOTTOM, 3, 4, "       ", handStr);
            prints(BOTTOM, 3, 4, "%s  ", handStr);
        }
        else
        {
            ConvertHand(Game.Players[player].Cards, 2, handStr);
            prints(BOTTOM, 3, 4, "%s", handStr);
        }

        if(NumCards == 4)
        {
            if(Game.Players[player].Folded)
            {
                ConvertHandSmall(Game.Players[player].Cards + 2, 2, handStr);
                prints(BOTTOM, 3, 6, "       ", handStr);
                prints(BOTTOM, 3, 5, "%s  ", handStr);
            }
            else
            {
                ConvertHand(Game.Players[player].Cards + 2, 2, handStr);
                prints(BOTTOM, 3, 6, "%s", handStr);
            }
        }
    }
    else
    {
        prints(BOTTOM, 3, 4, "       ");
        if(NumCards == 4)
        {
            prints(BOTTOM, 3, 6, "       ");
        }
    }
}

void Pause(int frames)
{
    static touchPosition touch;
    static int pressed;
    static int monteCarloSetup = 8;

    if(frames > 60 && monteCarloSetup > 2)
    {
        // Use some pause time to set up random hands for MonteCarlo.
        SetupMonteCarlo(monteCarloSetup--);
        frames -= 55;
    }

    while(frames--)
    {
        pressed = KeyPressed(&touch);
        if(pressed & KEY_TOUCH)
        {
            if(ShowingReplay)
            {
                ShowReplay(false);
            }
            else if(touch.px >= 2 && touch.px <= 37 && touch.py >= 56 && touch.py <= 71)
            {
                SwitchSitOut();
            }
        }
        swiWaitForVBlank();
    }
}

void WaitForTouch()
{
    while(1)
    {
        int pressed = KeyPressed();
        if(pressed & KEY_TOUCH)
        {
            return;
        }
    }
}

void RevealHoleCards(int player, bool clear)
{
    char handStr[10];

    DisplayCards(player, false);
    if(!clear)
    {
        ConvertHand(Game.Players[player].Cards, 2, handStr);
        prints(TOP, Seats[MapSeat(player)].PlayerX, Seats[MapSeat(player)].PlayerY + 2, "%*s", Seats[MapSeat(player)].Width, clear ? "" : handStr);
        if(NumCards == 2)
        {
			if(Game.MaxPlayers < MAX_PLAYERS)
			{
				prints(TOP, Seats[MapSeat(player)].PlayerX, Seats[MapSeat(player)].PlayerY + 4, "%*s", Seats[MapSeat(player)].Width, "");
			}
        }
        else
        {
            ConvertHand(Game.Players[player].Cards + 2, 2, handStr);
            prints(TOP, Seats[MapSeat(player)].PlayerX, Seats[MapSeat(player)].PlayerY + 4, "%*s", Seats[MapSeat(player)].Width, clear ? "" : handStr);
        }
    }
}

int TimeInSeconds(void)
{
    int seconds = 0;

    while(!seconds)
    {
        time_t unixTime = time(NULL);
        struct tm* timeStruct = gmtime((const time_t *)&unixTime);

        seconds += timeStruct->tm_hour * 3600;
        seconds += timeStruct->tm_min * 60;
        seconds += timeStruct->tm_sec;
    }

    return seconds;
}

void DisplayOptions(int numOptions, char **options, int x, int y, int w, int highlight = -1)
{
    for(int f = 0 ; f < numOptions ; f++)
    {
        HorizLine(BOTTOM, x, y++, w, true);
        VertLine(BOTTOM, x - 1, y, 2, true);
        VertLine(BOTTOM, x + w, y, 2, false);
        if(f == highlight)
        {
            Inverse = true;
        }
        prints(BOTTOM, x, y++, " %-*s", w - 1, options[f]);
        Inverse = false;
        HorizLine(BOTTOM, x, ++y, w, false);
        y++;
    }
}

int GetMenuOption(int numOptions, int defaultOption, ...)
{
    va_list argptr;
    uint longest = 0;
    int f;
    char *options[numOptions];

    // Find longest option first.
    va_start(argptr, defaultOption);
    for(f = 0 ; f < numOptions ; f++)
    {
        options[f] = va_arg(argptr, char *);
        uint len = strlen(options[f]);
        if(len > longest)
        {
            longest = len;
        }
    }
    va_end(argptr);

    longest += 2;

    Cls(BOTTOM);

    uint x = (32 - longest) / 2 + 1;
    uint y = (22 - (4 * numOptions)) / 2;
    uint topOption = y + 1;

    int selected = defaultOption;
    DisplayOptions(numOptions, options, x, y, longest, selected);

    touchPosition touch;
    while(1)
    {
        int pressed = KeyPressed(&touch);

        if(pressed & KEY_TOUCH)
        {
            uint xpos = (touch.px + 1) / 8;
            int ypos = touch.py - topOption * 8;

            if(xpos >= x &&    xpos <= x + longest - 1 &&
                ypos >= 0)
            {
                if(ypos % 32 < 17)
                {
                    selected = ypos / 32;
                    break;
                }
            }
        }
        else if(pressed & KEY_UP && selected > 0)
        {
            selected--;
            DisplayOptions(numOptions, options, x, y, longest, selected);
        }
        else if(pressed & KEY_DOWN && selected < numOptions - 1)
        {
            selected++;
            DisplayOptions(numOptions, options, x, y, longest, selected);
        }
        else if(pressed & KEY_A)
        {
            break;
        }
        WaitForVBlank();
    }
    return selected;
}

int KeyPressed(touchPosition *touch)
{
    scanKeys();
    if(touch)
    {
        touchRead(touch);
    }
    return keysDown();
}

void GetPlayerName(char *name)
{
    static char *rows[3] = { (char *)"QWERTYUIOP", (char *)"ASDFGHJKL", (char *)"ZXCVBNM.." };

    // Hack in the backspace and newline chars
    rows[2][7] = 225;
    rows[2][8] = 226;

    int nameLen = strlen(name);

    Cls(BOTTOM);
    DisplayKeys(2, 9,  rows[0]);
    DisplayKeys(3, 13, rows[1]);
    DisplayKeys(4, 17, rows[2]);

    HorizLine(BOTTOM, 12, 4, 8, true);
    HorizLine(BOTTOM, 12, 7, 8, false);
    VertLine(BOTTOM, 11, 5, 2, true);
    VertLine(BOTTOM, 20, 5, 2, false);

    touchPosition touch;
    while(1)
    {
        int pressed = KeyPressed(&touch);
        if(pressed & KEY_TOUCH)
        {
            uint xpos = touch.px;
            int ypos = touch.py - 9 * 8;

            if(ypos >= 0 && ypos % 32 < 17)
            {
                ypos /= 32;
                xpos -= (ypos + 1) * 8;
                xpos /= 24;
                if(ypos < 3 && xpos < strlen(rows[ypos]))
                {
                    if(ypos == 2 && xpos == 7)
                    {
                        if(nameLen)
                        {
                            name[--nameLen] = '\0';
                        }
                    }
                    else if(ypos == 2 && xpos == 8)
                    {
                        if(nameLen)
                        {
                            break;
                        }
                    }
                    else if(nameLen < 8)
                    {
                        name[nameLen++] = rows[ypos][xpos];
                        name[nameLen] = '\0';
                    }
                }
            }
            else
            {
                ClearRows(BOTTOM, 2, 2);
            }
        }
        Inverse = true;
        prints(BOTTOM, 12, 5, "%-8s", name);
        Inverse = false;
        WaitForVBlank();
    }

    FILE* outf = fopen("fat1:/ndsPokerPlayer.dat", "w");
    if(outf)
    {
        fprintf(outf, "%s", name);
        fclose(outf);
    }
}

void InitScreen(void)
{
    //initialize the backgrounds
    videoSetMode(MODE_5_2D);
    vramSetBankA(VRAM_A_MAIN_BG);
    bg0 = bgInit(0, BgType_Text4bpp, BgSize_ER_256x256, 9, 0);
    dmaCopy(tilesTiles, bgGetGfxPtr(bg0), sizeof(tilesTiles));
    dmaCopy(tilesPal, BG_PALETTE, sizeof(tilesPal));
    map = (u16*)bgGetMapPtr(bg0);

    videoSetModeSub(MODE_5_2D);
    vramSetBankC(VRAM_C_SUB_BG);
    bg0 = bgInitSub(0, BgType_Text4bpp, BgSize_ER_256x256, 9, 0);
    bg1 = bgInitSub(1, BgType_Text4bpp, BgSize_ER_256x256, 10, 0);
    dmaCopy(tilesTiles, bgGetGfxPtr(bg0), sizeof(tilesTiles));
    dmaCopy(tilesPal, BG_PALETTE_SUB, sizeof(tilesPal));
    subMap = (u16*)bgGetMapPtr(bg0);
    subMap2 = (u16*)bgGetMapPtr(bg1);

    bgHide(bg1);
    bgShow(bg0);

    *(BG_PALETTE + 16 + 15) = YELLOW;
    *(BG_PALETTE_SUB + 16 + 15) = YELLOW;
}

void SeatPlayers(void)
{
    int f;
    for(f = 0 ; f < Game.MaxPlayers ; f++)
    {
		DisplayPlayer(f, false);
    }
}

void UpdateScreens()
{
    int f;

    DrawTable();
    SeatPlayers();

    if(Game.AllIn)
    {
        SetAllIn();
        DisplayCommunityCards(Game.CardsShowing, TOP);
    }
    else
    {
        for(f = 0 ; f < Game.MaxPlayers ; f++)
        {
            DisplayBet(f, Game.Players[f].Bet);
            DisplayPlayer(f, f == Game.ActionOnPlayer);
            DisplayCards(f, !(Game.Players[f].Folded || (Game.Players[f].SitOutHands > 0)));
        }
        DisplayHoleCards(ThisPlayer);
        DisplayCommunityCards(Game.CardsShowing, BOTTOM);
    }
    DisplayPot();
}

void Beep(void)
{
    static bool once = false;

    if(!once)
    {
        soundEnable();
    }

    int soundId = soundPlayPSG(DutyCycle_87, 3000, 127, 64); 
    swiWaitForVBlank();
    swiWaitForVBlank();
    swiWaitForVBlank();
    soundKill(soundId);
}
